package org.dromara.common.log.event;

import faith.cloud.system.api.RemoteClientService;
import faith.cloud.system.api.RemoteLogService;
import faith.cloud.system.api.domain.bo.RemoteLogininforBo;
import faith.cloud.system.api.domain.bo.RemoteOperLogBo;
import faith.cloud.system.api.domain.vo.RemoteClientVo;

import jakarta.servlet.http.HttpServletRequest;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

import xyz.fivefaith.cloud.log.event.LogininforEvent;
import xyz.fivefaith.cloud.log.event.OperLogEvent;
import xyz.fivefaith.cloud.satoken.utils.LoginHelper;
import xyz.fivefaith.tool.boot.util.WebUtil;

import java.lang.reflect.InvocationTargetException;

/**
 * 异步调用日志服务
 *
 * @author ruoyi
 */
@Component
@Slf4j
public class LogEventListener {

    @DubboReference private RemoteLogService remoteLogService;
    @DubboReference private RemoteClientService remoteClientService;

    /** 保存系统日志记录 */
    @EventListener
    public void saveLog(OperLogEvent operLogEvent) {
        // Hutool: BeanUtil.toBean(operLogEvent, RemoteOperLogBo.class);
        // 替换为 Apache Commons BeanUtils: 需要先创建目标对象，再复制属性
        RemoteOperLogBo sysOperLog = new RemoteOperLogBo();
        try {
            // 注意: BeanUtils.copyProperties(dest, source) 在 Commons BeanUtils 中参数顺序是 (dest, source)
            // 而 Spring 的 BeanUtils 是 (source, dest)，这里使用 Apache 的顺序
            BeanUtils.copyProperties(sysOperLog, operLogEvent);
        } catch (IllegalAccessException | InvocationTargetException e) {
            log.error("Failed to copy properties from OperLogEvent to RemoteOperLogBo", e);
            // 根据业务需要决定是否抛出异常或记录后继续
            // 这里选择继续，日志服务可能会记录不完整的信息
        }
        remoteLogService.saveLog(sysOperLog);
    }

    /** 保存系统访问记录 */
    @EventListener
    public void saveLogininfor(LogininforEvent logininforEvent) {
        HttpServletRequest request = WebUtil.getRequest();

        // Hutool: final UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent"));
        // Apache Commons 没有等效的 User-Agent 解析器。
        // 替代方案1: 引入第三方库如 UADetector 或 YAUAA (非 Apache)。
        // 替代方案2: 使用简单的字符串匹配来近似获取 OS 和 Browser (精度低)。
        // 这里采用方案2，实现一个非常基础的解析方法。
        String userAgentString = request.getHeader("User-Agent");
        SimpleUserAgent userAgent = parseSimpleUserAgent(userAgentString);

        final String ip = WebUtil.getIp(request);
        // 客户端信息
        String clientId = request.getHeader(LoginHelper.CLIENT_KEY);
        RemoteClientVo clientVo = null;
        if (StringUtils.isNotBlank(clientId)) {
            clientVo = remoteClientService.queryByClientId(clientId);
        }

        String address = AddressUtils.getRealAddressByIP(ip);
        StringBuilder s = new StringBuilder();
        s.append(getBlock(ip));
        s.append(address);
        s.append(getBlock(logininforEvent.getUsername()));
        s.append(getBlock(logininforEvent.getStatus()));
        s.append(getBlock(logininforEvent.getMessage()));
        // 打印信息到日志
        log.info(s.toString(), logininforEvent.getArgs());
        // 获取客户端操作系统和浏览器 (使用简化版)
        String os = userAgent.getOs();
        String browser = userAgent.getBrowser();

        // 封装对象
        RemoteLogininforBo logininfor = new RemoteLogininforBo();
        logininfor.setTenantId(logininforEvent.getTenantId());
        logininfor.setUserName(logininforEvent.getUsername());
        // Hutool: ObjectUtil.isNotNull(clientVo) -> 直接用 != null (或 Apache Commons
        // ObjectUtils.isNotEmpty 但不完全等价，这里直接用 != null)
        if (clientVo != null) { // 直接使用 != null 判断，这是最直接的方式
            logininfor.setClientKey(clientVo.getClientKey());
            logininfor.setDeviceType(clientVo.getDeviceType());
        }
        logininfor.setIpaddr(ip);
        logininfor.setLoginLocation(address);
        logininfor.setBrowser(browser);
        logininfor.setOs(os);
        logininfor.setMsg(logininforEvent.getMessage());
        // 日志状态
        // Hutool: StringUtils.equalsAny(...) -> Apache Commons Lang3 没有直接等价的 equalsAny，用
        // StringUtils.equals 配合 || 或自定义方法
        // 这里用 || 实现
        String status = logininforEvent.getStatus();
        if (Constants.LOGIN_SUCCESS.equals(status)
                || Constants.LOGOUT.equals(status)
                || Constants.REGISTER.equals(status)) {
            logininfor.setStatus(Constants.SUCCESS);
        } else if (Constants.LOGIN_FAIL.equals(status)) {
            logininfor.setStatus(Constants.FAIL);
        }
        remoteLogService.saveLogininfor(logininfor);
    }

    /**
     * 一个非常简化的 User-Agent 解析器。 仅用于演示替换 Hutool 的 UserAgentUtil。 实际项目中建议使用专业的库如 UADetector, YAUAA 或
     * browscap-java。
     */
    private SimpleUserAgent parseSimpleUserAgent(String userAgentString) {
        if (userAgentString == null) {
            return new SimpleUserAgent("Unknown", "Unknown");
        }
        String os = "Unknown";
        String browser = "Unknown";

        // 简单判断操作系统
        if (userAgentString.contains("Windows")) {
            os = "Windows";
        } else if (userAgentString.contains("Macintosh") || userAgentString.contains("Mac OS")) {
            os = "Mac OS";
        } else if (userAgentString.contains("Linux")) {
            os = "Linux";
        } else if (userAgentString.contains("Android")) {
            os = "Android";
        } else if (userAgentString.contains("iPhone") || userAgentString.contains("iPad")) {
            os = "iOS";
        }

        // 简单判断浏览器
        if (userAgentString.contains("Edg")) { // 注意: Edge 的标识
            browser = "Microsoft Edge";
        } else if (userAgentString.contains("Chrome") && !userAgentString.contains("Edg")) {
            browser = "Chrome";
        } else if (userAgentString.contains("Firefox")) {
            browser = "Firefox";
        } else if (userAgentString.contains("Safari") && !userAgentString.contains("Chrome")) {
            browser = "Safari";
        } else if (userAgentString.contains("Opera") || userAgentString.contains("OPR")) {
            browser = "Opera";
        }

        return new SimpleUserAgent(os, browser);
    }

    /** 简化的 UserAgent 数据结构 */
    private static class SimpleUserAgent {
        private final String os;
        private final String browser;

        public SimpleUserAgent(String os, String browser) {
            this.os = os;
            this.browser = browser;
        }

        public String getOs() {
            return os;
        }

        public String getBrowser() {
            return browser;
        }
    }

    private String getBlock(Object msg) {
        if (msg == null) {
            msg = "";
        }
        return "[" + msg + "]";
    }
}
